package util;

import javafx.scene.control.Alert;
import model.PageTable;
import model.PageTableEntry;
import model.ContentBlock;
import model.DiskBlock;
import model.MemoryBlock;
import view.MemoryView;

import java.util.Map;

/**
 * @Create Harlantown
 * @Description
 * @create 2022-12-27 下午 8:11
 */
public class MemoryManage {
    // 内存块头部
    public static MemoryBlock headBlock;
    // 内存块数量
    public static int freeBlock = 64;

    static {
        headBlock = new MemoryBlock();
        headBlock.setPhysicalNumber(0);
        headBlock.setVisitTime(System.currentTimeMillis());
        headBlock.setCanChange(true);
        MemoryBlock ptr = headBlock;
        for (int i = 0; i < 63; i++) {
            MemoryBlock block = new MemoryBlock();
            block.setPhysicalNumber(i + 1);
            block.setVisitTime(System.currentTimeMillis());
            block.setCanChange(true);
            ptr.setNextBlock(block);
            ptr = block;
        }
    }

    static final Object lock = new Object();

    /*
    * 为新的文件分配内存
    * */
    public static int allocateMemory(PageTable pageTable) {
        int start = 0;
        //可以装的下新的八个快
        if (freeBlock >= 8) {
            synchronized (lock) {
                // 可用空间减少
                freeBlock -= 8;
                MemoryBlock ptr = headBlock;
                for (int i = 1; i <= 8; i++) {
                    // 找空闲区块
                    while (!ptr.isCanChange()) {
                        ptr = ptr.getNextBlock();
                    }
                    // 将新找到的区块加入到页表中
                    pageTable.add(new PageTableEntry(ptr, i, -1));
                    // 表示已经占有这块内存
                    ptr.setCanChange(false);
                    // 更新可视化
                    MemoryView.updateMemoryChartToUse(ptr.getPhysicalNumber());
                }
            }
            // 将新的页表加入页表链中
            PageTable.appendTableList(pageTable);
            return start;
        }
        // 装不下这个新文件
        Alert erralert = new Alert(Alert.AlertType.ERROR);
        erralert.setHeaderText(null);
        erralert.setContentText("内存不足！");
        erralert.showAndWait();
        return -1;
    }
    //
//                    Integer diskNum = indexMap.get(i);
//
//                    if (i == 1) {
//                        start = ptr.getPhysicalNumber();
//                    }
//                    if (diskNum != null) {
//                        // 填充内容、设置不可访问、更新修改时间
//                        ptr.addToMemoryBlock(diskBlocks[diskNum]);
//                    }else{
//                        diskNum = -1;
//                    }
//                    // 设置页表项
//                    ptr.setCanChange(false);
//
//                }
    // 剩余无论如何无法在第一次分配时载入的页面
//                for(int j = 9;;j++){
//                    Integer diskNum = indexMap.get(j);
//                    if (diskNum == null) {
//                        break;
//                    }
//                    pageTable.add(new PageTableEntry(j, diskNum));
//                }
    /*
    * 读取页
    * */
    public static ContentBlock readFromMemory(PageTable pageTable, int pageNum, DiskBlock[] diskBlocks, Map<Integer, Integer> indexMap) {

        // 如果要访问的页越界了
        if (pageNum > indexMap.size()) {
            // 弹出提示框
            Alert erralert = new Alert(Alert.AlertType.ERROR);
            erralert.setHeaderText(null);
            erralert.setContentText("页面访问越界！");
            erralert.showAndWait();
            return null;
        }
        // 尝试寻找该页是否已经在内存中
        for (int i = 0; i < pageTable.list.size(); i++) {
            // 找到该页对应的页表项
            if (pageTable.list.get(i).getLogicNum() == pageNum) {
                // 在内存中
                if (pageTable.list.get(i).isAvailableInMemory()) {
                    // 直接从内存中读取
                    int physicalBlockNumber = pageTable.list.get(i).getPhysicalBlockNumber();
                    Alert erralert = new Alert(Alert.AlertType.INFORMATION);
                    erralert.setHeaderText(null);
                    erralert.setContentText("页面在内存中，直接从内存中读取");
                    erralert.showAndWait();
                    return readFromMemoryBlock(physicalBlockNumber);
                }
            }
        }
        // 前面没找到，则代表缺页
        Integer diskAddress = indexMap.get(pageNum);
        // 调页
        int physicalBlockNumber = loadLoosePage(pageTable, diskBlocks[diskAddress], pageNum);
        return readFromMemoryBlock(physicalBlockNumber);
    }

    /*
    * 读取已经加载入内存的内存块
    * */
    public static ContentBlock readFromMemoryBlock(int physicalBlockNumber) {
        MemoryBlock ptr = headBlock;
        // 链表遍历
        for (int i = 0; i < physicalBlockNumber; i++) {
            if (ptr == null) {
                // 内存块越界
                return null;
            }
            ptr = ptr.getNextBlock();
        }
        // 刷新访问时间，为LRU算法做参考
        ptr.refreshVisitTime();
        return ptr.getContentBlock();
    }

    /*
    * 调页
    * */
    private static int loadLoosePage(PageTable pageTable, DiskBlock diskBlock, int pageNum) {
        PageTableEntry pageTableEntry = null;
        // 查看初始给给文件分配的文件块是否还有空闲
        for (int i = 0; i < pageTable.list.size(); i++) {
            // 指向的磁盘地址为-1，有空闲
            if (pageTable.list.get(i).getDiskAddress() == -1) {
                // 定位到该内存块
                pageTableEntry = pageTable.list.get(i);
                pageTableEntry.setLogicNum(pageNum);
                pageTableEntry.setAvailableInMemory(true);
                // 显示提示信息
                Alert erralert = new Alert(Alert.AlertType.INFORMATION);
                erralert.setHeaderText(null);
                erralert.setContentText("使用该文件分配的块加载！");
                erralert.showAndWait();
                break;
            }
        }
        // 给文件分配的文件块已用完
        if (pageTableEntry == null) {
            pageTableEntry = new PageTableEntry();
            // 利用LRU全局置换寻找待置换的文件块
            int blockAddr = lru(diskBlock);
            // 更新页表项
            pageTableEntry.setAvailableInMemory(true);
            pageTableEntry.setPhysicalBlockNumber(blockAddr);
            pageTableEntry.setPageNumber(pageTable.list.size() + 1);
            pageTableEntry.setLogicNum(pageNum);
            pageTable.list.add(pageTableEntry);
            // 显示提示信息
            Alert erralert = new Alert(Alert.AlertType.INFORMATION);
            erralert.setHeaderText(null);
            erralert.setContentText("发生全局置换！！置换的块数为：" + blockAddr);
            erralert.showAndWait();
        }

        MemoryBlock ptr = headBlock;
        // 遍历链表，拿到需要交换的内存块
        for (int i = 0; i < pageTableEntry.getPhysicalBlockNumber(); i++) {
            ptr = ptr.getNextBlock();
        }

        // 更新页表项
        ptr.setContentBlock(new ContentBlock(40, diskBlock.getContentP()));
        ptr.setCanChange(false);
        pageTableEntry.setPhysicalBlockNumber(ptr.getPhysicalNumber());
        pageTableEntry.setAvailableInMemory(true);
        pageTableEntry.setDiskAddress(diskBlock.getNo());
        // 更新访问时间
        refreshVisitTime(ptr.getPhysicalNumber());
        // 更新可视化视图
        MemoryView.updateMemoryChartToUse(ptr.getPhysicalNumber());
        return ptr.getPhysicalNumber();
    }

    /*
    * LRU页面置换算法（执行）
    * */
    private static int lru(DiskBlock diskBlock) {
        // 利用LRU寻找算法，调用函数寻找全局需要调换（或空闲）的内存块
        MemoryBlock lruBlock = findLruBlock();
        // 反找页表地址
        PageTable ptr = PageTable.pageTableHead;
        // 遍历每一个链表
        while (ptr != null) {
            // 遍历每一个页表
            for (int i = 0; i < ptr.list.size(); i++) {
                PageTableEntry pageTableEntry = ptr.list.get(i);
                // 找到了待置换的内存块对应的页表
                if (pageTableEntry.isAvailableInMemory() &&
                        pageTableEntry.getPhysicalBlockNumber() == lruBlock.getPhysicalNumber()) {
                    // 更新页表项，剔除这个块
                    pageTableEntry.setAvailableInMemory(false);
                    pageTableEntry.setPhysicalBlockNumber(-1);
                }
            }
            ptr = ptr.getNextTable();
        }
        // 调入内容
        lruBlock.setContentBlock(new ContentBlock(40, diskBlock.getContentP()));
        lruBlock.setCanChange(false);
        //更新访问时间
        lruBlock.setVisitTime(System.currentTimeMillis());
        return lruBlock.getPhysicalNumber();
    }

    public static void refreshVisitTime(int blockNum) {
        MemoryBlock ptr = headBlock;
        for (int i = 0; i < blockNum; i++) {
            ptr = ptr.getNextBlock();
        }
        ptr.setVisitTime(System.currentTimeMillis());
    }

    /*
    * LRU寻找目标块算法
    * */
    public static MemoryBlock findLruBlock() {
        // 需加锁
        Object lock = new Object();
        // 指针
        MemoryBlock ptr = headBlock;
        // 保存结果
        MemoryBlock res = headBlock;
        // 存储当前最小的时间
        long currentTime = headBlock.getVisitTime();

        synchronized (lock) {
            // 链表遍历寻找待替换的块
            while (ptr != null) {
                // 一个空闲的块，直接返回
                if (ptr.isCanChange()) {
                    freeBlock -= 1;
                    return ptr;
                }
                // 当前块不空闲，判断最近访问时间
                if (ptr.getVisitTime() < currentTime && !ptr.isCanChange()) {
                    currentTime = ptr.getVisitTime();
                    res = ptr;
                }
                ptr = ptr.getNextBlock();
            }
        }
        return res;
    }

    public static void removeBlock() {
        PageTable ptr = PageTable.pageTableHead;
        while (ptr != null) {
            for (int i = 0; i < ptr.list.size(); i++) {
                PageTableEntry pageTableEntry = ptr.list.get(i);
                if (pageTableEntry.isAvailableInMemory()) {
                    int physicalBlockNumber = pageTableEntry.getPhysicalBlockNumber();
                    MemoryBlock mptr = headBlock;
                    for (int j = 0; j < physicalBlockNumber; j++) {
                        mptr = mptr.getNextBlock();
                    }
                    // 不对具体内容做处理
                    mptr.setCanChange(true);
                }
            }
            ptr = ptr.getNextTable();
        }
    }

    /*
    * 回收内存
    * */
    public static void removeFile(PageTable pageTable) {
        // 返还空闲块数量
        freeBlock += pageTable.list.size();
        // 遍历当前进程页表
        for (int i = 0; i < pageTable.list.size(); i++) {
            PageTableEntry pageTableEntry = pageTable.list.get(i);
            // 对于每一个被占用的内存块
            int physicalBlockNumber = pageTableEntry.getPhysicalBlockNumber();
            // 定位到具体内存块
            MemoryBlock ptr = headBlock;
            for (int j = 0; j < physicalBlockNumber; j++) {
                ptr = ptr.getNextBlock();
            }
            // 标记该内存块已空闲，不对具体内容做处理
            ptr.setCanChange(true);
            // 更新可视化
            MemoryView.updateMemoryChartToUnUse(pageTableEntry.getPhysicalBlockNumber());
        }
        // 删除这个文件的页表
        if (pageTable.getPreTable() != null) {
            pageTable.getPreTable().setNextTable(pageTable.getNextTable());
        }
        if (pageTable.getNextTable() != null) {
            pageTable.getNextTable().setPreTable(pageTable.getPreTable());
        }
        return;
    }


}
